"""A simple graphical level editor."""

__author__ = 'Josh Oosterman'

import pygame
import pygame.key
import pygame.mouse

from potv import engine
from potv import geom
from potv import input
from potv.game import building
from potv.game import terrain


class EditorControl(object):
  """Allows the user to place terrain etc with the mouse."""
  SCROLL_SPEED = 5

  def __init__(self, filename):
    self.depth = 0
    self.modes = {}
    self.modes[1] = (
        'terrain',
        self._AdjustTerrain)
    self.modes[2] = (
        'road',
        self._AdjustRoad)
    self.modes[3] = (
        'object',
        self._PlaceObject)
    self.mode = 1
    self.filename = filename

  def Setup(self, terrain_layer, road_layers):
    # Create a terrain control.
    self.terrain_matrix = terrain_layer
    terrain_tile_images = terrain.TerrainBuilder(
        self.terrain_matrix).Build()
    self.terrain_control = terrain.TileRenderer(
        terrain_tile_images, terrain.TERRAIN_SIZE)
    self.level.AddObject(self.terrain_control)

    # Create a road control
    # TODO(joosterman): Can have more than one road layer.
    self.road_type, self.road_matrix = road_layers[0]
    road_tile_images = terrain.RoadBuilder(
        self.road_type, self.road_matrix).Build()
    self.road_control = terrain.TileRenderer(
        road_tile_images, terrain.ROAD_SIZE)
    self.level.AddObject(self.road_control)

  def Step(self):
    # Move the view around the room using the w/a/s/d keys.
    motion = geom.Vector(0, 0)
    if input.IsDown(input.EDITOR_LEFT):
      motion.x -= 1
    if input.IsDown(input.EDITOR_RIGHT):
      motion.x += 1
    if input.IsDown(input.EDITOR_UP):
      motion.y -= 1
    if input.IsDown(input.EDITOR_DOWN):
      motion.y += 1
    motion = motion.Normalize() * self.SCROLL_SPEED
    self.level.views[0].world_location += motion
    self.level.views[0].KeepInsideLevel()

    for i in self.modes:
      keycode = getattr(pygame, 'K_%s' % i)
      if input.WasPressed(keycode):
        self.mode = i

    # If the mouse button was pressed, then call the current action.
    if input.WasMousePressed():
      pos = input.GetMouseWorldPosition(self.level)
      self.modes[self.mode][1](pos)

    if input.IsDown(input.EDITOR_SAVE):
      self.Save()

  def _GridIndex(self, position, grid_size):
    """Gets the grid x, y index at a given point."""
    return int(position.x / grid_size), int(position.y / grid_size)

  def _Snap(self, position, grid_size):
    """Snaps a position to a grid."""
    x, y = self._GridIndex(position, grid_size)
    return x * grid_size, y * grid_size

  def _AdjustTerrain(self, position):
    """Flips the tile index under the mouse."""
    x, y = self._GridIndex(position, terrain.TERRAIN_SIZE)
    self.terrain_matrix[y][x] = (self.terrain_matrix[y][x] + 1) % 3

    # Regenerate the terrain.
    terrain_tile_images = terrain.TerrainBuilder(self.terrain_matrix).Build()
    self.terrain_control.tile_images = terrain_tile_images

  def _AdjustRoad(self, position):
    """Flips the road presense/absence under the mouse."""
    x, y = self._GridIndex(position, terrain.ROAD_SIZE)
    self.road_matrix[y][x] = 1 - self.road_matrix[y][x]

    # Regenerate the roads.
    road_tile_images = terrain.RoadBuilder('asphalt', self.road_matrix).Build()
    self.road_control.tile_images = road_tile_images

  def _PlaceObject(self, position):
    """Places an object in the level."""
    x, y = self._Snap(position, terrain.TERRAIN_SIZE * 2)
    self.level.AddObject(building.Building(location=geom.Vector(x + 3, y + 3)))

  def Save(self):
    """Saves the current level out to disk."""
    bits = []
    bits.append('# Level definition.')
    bits.append('Meta(width=%d,height=%d, name="")' % (
        self.level.size.x, self.level.size.y))
    bits.append('TerrainLayer(\n  %s)' % self.terrain_matrix)
    bits.append('RoadLayer(\'%s\',\n  %s)' % (self.road_type, self.road_matrix))
    bits.append('# Init objects.')

    object_lines_by_type = {}
    for object in self.level.objects:
      classname = object.__class__.__name__
      if classname in ('Player', 'Building', 'Car'):
        line = '%s(location=(%d,%d))' % (
            classname, object.location.x, object.location.y)
        object_lines_by_type.setdefault(classname, []).append(line)
    for classname, object_lines in object_lines_by_type.iteritems():
      bits.append('\n'.join(object_lines))

    with open(self.filename, 'w') as f:
      # Force newlines on long lists.
      bits = [b.replace(', [', ',\n   [') for b in bits]
      f.write('\n\n'.join(bits))
